﻿/*
// Author:		Sebastián Endler
// Create date: 2010/05/04
// This control requires the following tables:
// 0 - You must to include Table Definition as the first Table,is a mandatory table 
// This table must contains the following Columns: 
//      -TableIndex --> INT
//      -Name ['Variables','AnalogValues','DigitalValues','Events','Sections','Options'] --STRING VALUE.
//
//The position for the next tables depend on Table Definition: 
//[n] - Variables Definition [ID, Name, MinScale, MaxScale, IsDigital, Style, Color, Pane] until now.
//[n] - AnalogValues [ID,XValue,YValue]
//[n] - DigitalValues [ID,Start,End,Description]
//[n] - Events [EventType, XValue, Description]
//[n] - Sections [Start, End, Title, BorderThickness, BorderColor, BackgroundColor]
//[n] - Options [Name, Value] //by Default XAxisIsTime is true;
//
//
//The column "Style", and "Pane" in the VariablesDefinition Table, is only apply for Analog Curves Values // until now.
//Style:
//  0 - Use Linear function to draw the curve.
//  1 - Use Quadratic function to draw the curve.
//  2 - Use Bezier function to draw the curve.

//Pane allow to use different pane for each curve.
*/
(function () {
    function _IndustrialChart(object, options) {
        try {
            var DEFAULT_HEIGHT = 500;
            var DEFAULT_WIDTH = 800;

            var PRIVATEPARAMETERS, CANVAS, CTX, MAXVALUE_X, MINVALUE_X, LOADINGSHAPE, VARIABLELIST;
            var DIGITAL_Y, EVENTSIZEFACTOR, ISDIGITALPANELACTIVE, LOADINGCONTAINER, LOADINGIMAGE, LOADINGTITLE, VARIABLELIST, UXINDUSTRIALCHARTCONTAINER;
            var DEFAULTFONTSTYLE, XAXISISTIME, COLORS, BORDERLINETHICKNESS, ANALOGCURVESTHICKNESS, DIGITALCURVESTHICKNES, REGEXPDATE, DEFAULTLINESTYLE, LINETYPES;
            /* MEASUREMENT*/
            var HEIGHT, WIDTH, HEIGHT_SCALEAREA, WIDTH_SCALEAREA, HEIGHT_DRAWAREA, WIDTH_DRAWAREA, HEIGHT_AXISXSCALEAREA, WIDTH_AXISXSCALEAREA, HEIGHT_PANE;
            var HEIGHT_DIGITALAREA, WIDTH_DIGITALAREA, HEIGHT_REFERENCEAREA, WIDTH_REFERENCEAREA, HEIGHT_CONTAINER, WIDTH_CONTAINER, DEFAULTVARIABLESCALEWIDTH;
            var MARGINTOP, MARGINLEFT, MARGINBOTTOM, MARGINRIGHT, MARGINPANE;
            var MYTIMER, ISCOMPLETE, RESIZETICKTIMES, PANECOUNT;
            function _initializeConstAndDefaultValues(_width, _height) {
                try {
                    //DEFAULTVALUES
                    if (_width == undefined) _width = DEFAULT_WIDTH;
                    if (_height == undefined) _height = DEFAULT_HEIGHT;
                    MARGINTOP = 25;
                    MARGINLEFT = 0;
                    MARGINRIGHT = 10;
                    MARGINBOTTOM = 0;
                    MAXVALUE_X = -99999999999999;
                    MINVALUE_X = 999999999999999;
                    DEFAULTVARIABLESCALEWIDTH = 50;
                    WIDTH_CONTAINER = _width;
                    HEIGHT_CONTAINER = _height;
                    WIDTH = WIDTH_CONTAINER - 10;
                    HEIGHT = HEIGHT_CONTAINER - 33;
                    MARGINPANE = 10;
                    PANECOUNT = 0;
                    var RESTHEIGHT = 0;
                    if (HEIGHT < 450) {
                        HEIGHT_DRAWAREA = HEIGHT_SCALEAREA = parseInt((HEIGHT - (MARGINTOP + MARGINBOTTOM)) * .66);
                        WIDTH_SCALEAREA = DEFAULTVARIABLESCALEWIDTH; //DEFAULT (ONE SCALE);
                        WIDTH_REFERENCEAREA = WIDTH_DIGITALAREA = WIDTH_AXISXSCALEAREA = WIDTH_DRAWAREA = (WIDTH - (MARGINLEFT + MARGINRIGHT + WIDTH_SCALEAREA));
                        RESTHEIGHT = parseInt((HEIGHT - (MARGINTOP + MARGINBOTTOM)) * .33);
                        HEIGHT_AXISXSCALEAREA = parseInt(RESTHEIGHT * .30);
                        HEIGHT_DIGITALAREA = parseInt(RESTHEIGHT * .35);
                        HEIGHT_REFERENCEAREA = parseInt(RESTHEIGHT * .28);
                    } else {
                        HEIGHT_DRAWAREA = HEIGHT_SCALEAREA = parseInt((HEIGHT - (MARGINTOP + MARGINBOTTOM)) * .66);
                        WIDTH_SCALEAREA = DEFAULTVARIABLESCALEWIDTH; //DEFAULT (ONE SCALE);
                        WIDTH_REFERENCEAREA = WIDTH_DIGITALAREA = WIDTH_AXISXSCALEAREA = WIDTH_DRAWAREA = (WIDTH - (MARGINLEFT + MARGINRIGHT + WIDTH_SCALEAREA));
                        RESTHEIGHT = parseInt((HEIGHT - (MARGINTOP + MARGINBOTTOM)) * .33);
                        HEIGHT_AXISXSCALEAREA = parseInt(RESTHEIGHT * .30);
                        HEIGHT_DIGITALAREA = parseInt(RESTHEIGHT * .35);
                        HEIGHT_REFERENCEAREA = parseInt(RESTHEIGHT * .28);
                    }
                    DIGITAL_Y = 10;
                    EVENTSIZEFACTOR = 1.5;
                    ISDIGITALPANELACTIVE = false;
                    ISCOMPLETE = false;
                    RESIZETICKTIMES = 250;
                    DEFAULTFONTSTYLE = {
                        size: 6,
                        font: null,
                        color: '#252525',
                        weight: 1,
                        halign: 'l',      // l: left, r: right, c: center
                        valign: 'b',      // t: top, m: middle, b: bottom 
                        adjustAlign: false, // modifies the alignments if the angle is different from 0 to make the spin point always at the good position
                        angle: 0,         // in radians, anticlockwise
                        tracking: 1,      // space between the letters, float, 1 for 'normal'
                        boundingBoxColor: '#ff0000', //null // color of the bounding box (null to hide), can be used for debug and font drawing
                        originPointColor: '#000000' //null // color of the bounding box (null to hide), can be used for debug and font drawing
                    };
                    XAXISISTIME = true;
                    COLORS = {};
                    COLORS.LINES = [{ fillColor: '#F02020', fontColor: '#2D2A2A' }, { fillColor: '#1C17DA', fontColor: '#2D2A2A' }
                                    , { fillColor: '#D1C835', fontColor: '#2D2A2A' }, { fillColor: '#29D93E', fontColor: '#2D2A2A' }
                                    , { fillColor: '#36C3C5', fontColor: '#2D2A2A' }, { fillColor: '#D63BC2', fontColor: '#2D2A2A' }
                                    , { fillColor: '#E6811E', fontColor: '#2D2A2A' }, { fillColor: '#3D4A2F', fontColor: '#2D2A2A' }
                                    , { fillColor: '#118D7D', fontColor: '#2D2A2A' }, { fillColor: '#6227BA', fontColor: '#2D2A2A' }
                                   ];
                    COLORS.EVENTS = [{ fillColor: '#ECB339', borderColor: '#625F5F' }, { fillColor: '#6339EC', borderColor: '#625F5F' }
                                    , { fillColor: '#F94C24', borderColor: '#625F5F' }, { fillColor: '#E2D617', borderColor: '#625F5F' }
                                    , { fillColor: '#15DBED', borderColor: '#625F5F' }, { fillColor: '#D63BC2', borderColor: '#625F5F' }
                                    , { fillColor: '#E6811E', borderColor: '#625F5F' }, { fillColor: '#3D4A2F', borderColor: '#625F5F' }
                                    , { fillColor: '#118D7D', borderColor: '#625F5F' }, { fillColor: '#6227BA', borderColor: '#625F5F' }
                                    ];
                    COLORS.SECTIONS = [{ fillColor: 'rgba(157,240,107,0.2)', borderColor: '#625F5F' }, { fillColor: 'rgba(102,202,184,0.2)', borderColor: '#625F5F' }
                                        , { fillColor: 'rgba(140,221,238,0.2)', borderColor: '#625F5F' }, { fillColor: 'rgba(180,130,180,0.2)', borderColor: '#625F5F' }
                                        , { fillColor: 'rgba(232,238,140,0.2)', borderColor: '#625F5F' }, { fillColor: 'rgba(115,180,112,0.2)', borderColor: '#625F5F' }
                                        , { fillColor: 'rgba(243,106,154,0.2)', borderColor: '#625F5F' }, { fillColor: 'rgba(220,170,120,0.2)', borderColor: '#625F5F' }
                                        , { fillColor: 'rgba(179,202,135,0.2)', borderColor: '#625F5F' }, { fillColor: 'rgba(250,110,154,0.2)', borderColor: '#625F5F' }
                                    ];
                    COLORS.GRID = [{ borderColor: '#808080'}];
                    BORDERLINETHICKNESS = 1;
                    ANALOGCURVESTHICKNESS = 1;
                    DIGITALCURVESTHICKNES = 10;
                    DEFAULTLINESTYLE = 0;
                    REGEXPDATE = '/Date\(|\)//'; // /\/Date\(|\)/;
                    REGEXPDATE2 = '[0-9]{13}';
                    LINETYPES = ['line', 'quadratic', 'bezier'];
                    VARIABLELIST = [];
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _getElementSizeWidth(elem) {
                try {
                    if (elem) {
                        if (elem.offsetWidth != undefined) return elem.offsetWidth;
                        else if (elem.clientWidth != undefined) return elem.clientWidth;
                        else if (elem.parentElement) return _getParentSize(elem.parentElement);
                    }
                    else if (window.document.body.clientWidth != undefined) return window.document.body.clientWidth;
                    else if (window.document.body.offsetWidth != undefined) return window.document.body.offsetWidth;
                    else return DEFAULT_WIDTH;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _getElementSizeHeight(elem) {
                try {
                    if (elem) {
                        if (elem.offsetHeight != undefined) return elem.offsetHeight;
                        else if (elem.clientHeight != undefined) return elem.clientHeight;
                        else if (elem.parentElement) return _getParentSize(elem.parentElement);
                    }
                    else if (window.document.body.clientHeight != undefined) return window.document.body.clientHeight;
                    else if (window.document.body.offsetHeight != undefined) return window.document.body.offsetHeight;
                    else return DEFAULT_HEIGHT;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _getElementSize(elem) {
                try {
                    var _w; var _h;
                    if ((elem.style.width) && (elem.style.width.indexOf('px') > -1)) _w = parseInt(elem.style.width.replace('px', ''));
                    else if ((elem.style.width) && (elem.style.width.indexOf('%') > -1)) {
                        _w = parseInt(elem.style.width.replace('%', ''));
                        var psw = _getElementSizeWidth(elem);
                        _w = (_w * psw) / 100.0;
                    }
                    if ((elem.style.height) && (elem.style.height.indexOf('px') > -1)) _h = parseInt(elem.style.height.replace('px', ''));
                    else if ((elem.style.height) && (elem.style.height.indexOf('%') > -1)) {
                        _h = parseInt(elem.style.height.replace('%', ''));
                        var psh = _getElementSizeHeight(elem);
                        _h = (_h * psh) / 100.0;
                    }
                    return { width: _w, height: _h };
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _initialize(myDiv, options) {
                try {

                    var ges = _getElementSize(myDiv);
                    _initializeConstAndDefaultValues(ges.width, ges.height);
                    PRIVATEPARAMETERS = PrivateParameters(options);
                    if (PRIVATEPARAMETERS.events.onComplete == undefined) PRIVATEPARAMETERS.events.onComplete = function () { return; };
                    if ((PRIVATEPARAMETERS.allowResize) && (PRIVATEPARAMETERS.allowResize == true)) {
                        var fn = _resizeChart;
                        window.onresize = function () {
                            if (MYTIMER) clearTimeout(MYTIMER);
                            MYTIMER = setTimeout(fn, RESIZETICKTIMES);
                        }
                    }
                    PRIVATEPARAMETERS.version.Class = 'IndustrialChart';
                    PRIVATEPARAMETERS.container = myDiv;
                    var cc = _IndustrialChartContainer(myDiv);
                    if (cc) _initializeCanvas(cc);
                    else return null;
                    if (window.CanvasText)
                        CanvasText.enable(CTX);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _resizeChart() {
                try {
                    if (ISCOMPLETE) {
                        if (MYTIMER) clearTimeout(MYTIMER);
                        MYTIMER = null;
                        _clearArea();
                        var ges = _getElementSize(PRIVATEPARAMETERS.container);
                        var max = MAXVALUE_X;
                        var min = MINVALUE_X;
                        _initializeConstAndDefaultValues(ges.width, ges.height);
                        CANVAS.height = HEIGHT;
                        CANVAS.width = WIDTH;
                        CANVAS.style.height = HEIGHT + 'px';
                        CANVAS.style.width = WIDTH + 'px';
                        UXINDUSTRIALCHARTCONTAINER.style.width = WIDTH_CONTAINER + 'px';
                        UXINDUSTRIALCHARTCONTAINER.style.height = HEIGHT_CONTAINER + 'px';
                        MAXVALUE_X = max;
                        MINVALUE_X = min;
                        _redraw(PRIVATEPARAMETERS.requestData, true);
                    }
                    ISCOMPLETE = false;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _initializeCanvas(container) {
                try {
                    if (container) {
                        CANVAS = document.createElement('CANVAS');
                        CANVAS.height = HEIGHT;
                        CANVAS.width = WIDTH;
                        CANVAS.style.height = HEIGHT + 'px';
                        CANVAS.style.width = WIDTH + 'px';
                        container.appendChild(CANVAS);
                        LOADINGSHAPE = document.createElement('div');
                        container.appendChild(LOADINGSHAPE);
                        LOADINGSHAPE.className = 'loadingShape';
                        LOADINGSHAPE.style.display = 'none';
                        var loadingShapeImage = document.createElement('div');
                        loadingShapeImage.className = 'loadingImage';
                        LOADINGSHAPE.appendChild(loadingShapeImage);
                        var pText = document.createElement('p');
                        pText.innerHTML = 'Loading...';
                        LOADINGSHAPE.appendChild(pText);
                        pText.className = 'loadingShapeText';
                        if (Browser.IE) CANVAS = window.G_vmlCanvasManager.initElement(CANVAS);
                        CTX = CANVAS.getContext('2d');

                        //                        addEventHandler(CANVAS, 'onclick', function (e) {
                        //                            var target = e.target || e.srcElement;
                        //                            alert("x:" + (e.clientX - CANVAS.offsetLeft) + " y:" + (e.clientY - CANVAS.offsetTop));

                        //                        });
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _IndustrialChartContainer(container) {
                try {
                    if (container) {
                        var uxICContainer = document.createElement('div');
                        uxICContainer.className = 'industrialChartContainer';
                        uxICContainer.style.width = WIDTH_CONTAINER + 'px';
                        uxICContainer.style.height = HEIGHT_CONTAINER + 'px';
                        UXINDUSTRIALCHARTCONTAINER = uxICContainer;
                        var uxICHeader = document.createElement('div');
                        uxICHeader.className = 'ICHeader';
                        var uxICP = document.createElement('div');
                        uxICP.className = 'classTitle';
                        uxICP.innerHTML = 'Industrial Chart 2.0';
                        uxICHeader.appendChild(uxICP);
                        uxICContainer.appendChild(uxICHeader);
                        var uxICChartContainer = document.createElement('div');
                        uxICChartContainer.className = 'ICChartContainer';
                        uxICContainer.appendChild(uxICChartContainer);

                        var uxLoadingContainer = document.createElement('div');
                        LOADINGCONTAINER = uxLoadingContainer;
                        uxLoadingContainer.className = 'loadingContainer';
                        uxLoadingContainer.style.display = 'none';
                        uxICHeader.appendChild(uxLoadingContainer);

                        var uxLoadingImage = document.createElement('div');
                        uxLoadingImage.className = 'animate';
                        LOADINGIMAGE = uxLoadingImage;
                        uxLoadingContainer.appendChild(uxLoadingImage);

                        var uxLoadingTitle = document.createElement('div');
                        uxLoadingTitle.className = 'title';
                        LOADINGTITLE = uxLoadingTitle;
                        uxLoadingContainer.appendChild(uxLoadingTitle);
                        uxLoadingTitle.innerHTML = 'Loading...';

                        container.appendChild(uxICContainer);
                        return uxICChartContainer;
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawLoadingShape() {
                try {
                    LOADINGSHAPE.style.width = WIDTH + 'px';
                    LOADINGSHAPE.style.height = HEIGHT + 'px';
                    LOADINGSHAPE.style.marginTop = (HEIGHT * -1) + 'px';
                    LOADINGSHAPE.style.display = '';
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _displayInsideLoading() {
                try {
                    _drawRectangle(0, 0, WIDTH, HEIGHT, '#FF0000', 0, 'rgba(0,0,0,0.7)');
                    _drawLoadingShape();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _refreshData(parameters) {
                try {
                    LOADINGIMAGE.className = 'animate';
                    LOADINGTITLE.innerHTML = 'Loading...';
                    LOADINGCONTAINER.style.display = '';
                    _displayInsideLoading();
                    var ges = _getElementSize(PRIVATEPARAMETERS.container);
                    _initializeConstAndDefaultValues(ges.width, ges.height);
                    _requestData(parameters);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _requestData(parameters) {
                try {
                    var url = PRIVATEPARAMETERS.URL;
                    var options = {}; options.data = {};
                    options.data.DatabaseParameters = PRIVATEPARAMETERS.DatabaseParameters;
                    options.data.JSONFile = PRIVATEPARAMETERS.JSONFile;
                    for (var cp in PRIVATEPARAMETERS.connectionParameters)
                        options[cp] = PRIVATEPARAMETERS.connectionParameters[cp];
                    if ((parameters) && (parameters.Version))
                        options.data.DatabaseParameters.QueryParameters = parameters.getQueryParameters();
                    else options = (parameters) ? parameters : options;

                    options.data.Namespace = PRIVATEPARAMETERS.version.Namespace;
                    options.data.Class = PRIVATEPARAMETERS.version.Class;
                    options.onSuccess = function (requestData) {
                        if ((requestData) && (!requestData.message)) {
                            data = requestData.ModelData;
                            LOADINGCONTAINER.style.display = 'none';
                            LOADINGSHAPE.style.display = 'none';
                            _clearArea();
                            _redraw(data, false);
                        }
                        else {
                            LOADINGCONTAINER.style.display = '';
                            LOADINGTITLE.innerHTML = 'No available Data.';
                            LOADINGIMAGE.className = 'noAnimate';
                            LOADINGSHAPE.style.display = 'none';
                            PRIVATEPARAMETERS.container.innerHTML = '';
                        }
                    }
                    options.onFailure = function (message) {
                        PRIVATEPARAMETERS.events.onFailure(message);
                    }
                    RequestJSON(url, options);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _clearArea() {
                try {
                    CTX.clearRect(0, 0, WIDTH, HEIGHT);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawScale(height, width, cuts, start, end, sort, color, x, y, lineCap, textStyle) {
                try {
                    var textFontStyle = (textStyle) ? textStyle : DEFAULTFONTSTYLE;
                    CTX.strokeStyle = color;
                    CTX.beginPath();
                    CTX.lineWidth = 1;
                    CTX.moveTo(x, y);
                    CTX.lineTo(x, y + height);
                    CTX.stroke();
                    var y1 = y;
                    var cY = (height / (cuts - 1));
                    var p = ((sort) ? end - start : start - end) / (cuts - 1);
                    var text = ((sort) ? end : start);
                    //var inc = (end - start) / cuts;
                    for (var i = 0; i < cuts; i++) {
                        CTX.strokeStyle = color;
                        CTX.beginPath();
                        CTX.lineWidth = 0.5;
                        CTX.moveTo(x, y);
                        CTX.lineTo(width + x, y);
                        CTX.stroke();
                        text = parseInt(text * 100.00) / 100.00;
                        CTX.drawText(text.toString(), width + x, (y + (textFontStyle.size / 2)), textFontStyle);
                        text -= p;
                        y += cY;
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawAxisXScale(height, width, thickness, cuts, start, end, sort, color, x, y, lineCap, textStyle) {
                try {
                    var textFontStyle = (textStyle) ? textStyle : DEFAULTFONTSTYLE;
                    textFontStyle.size = 7;
                    textFontStyle.angle = 5.2;
                    textFontStyle.color = '#303030';
                    textFontStyle.weight = 1;
                    CTX.strokeStyle = color;
                    CTX.beginPath();
                    CTX.lineWidth = 1;
                    y += 3;
                    var x1 = 0;
                    var cX = (width / (cuts - 1));
                    var p = ((sort) ? end - start : start - end) / (cuts - 1);
                    var timeText;
                    //var text = ((sort) ? end : start);
                    //var inc = (end - start) / cuts;
                    for (var i = 0; i < cuts; i++) {
                        CTX.strokeStyle = color;
                        CTX.beginPath();
                        CTX.lineWidth = thickness;
                        CTX.moveTo(x + x1, y);
                        CTX.lineTo(x + x1, y + height);
                        CTX.stroke();
                        timeText = timetStringh(start + (p * i));
                        CTX.drawText(timeText.toString(), (x + x1) - (textFontStyle.size), (y + 35 + (textFontStyle.size / 2)), textFontStyle);
                        x1 += cX;
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawRoundedBorder(x, y, width, height, radius, color, lineWidth) {
                try {
                    CTX.beginPath();
                    CTX.strokeStyle = color;
                    CTX.lineWidth = lineWidth;
                    CTX.moveTo(x, y + radius);
                    CTX.lineTo(x, y + height - radius);
                    CTX.quadraticCurveTo(x, y + height, x + radius, y + height);
                    CTX.lineTo(x + width - radius, y + height);
                    CTX.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
                    CTX.lineTo(x + width, y + radius);
                    CTX.quadraticCurveTo(x + width, y, x + width - radius, y);
                    CTX.lineTo(x + radius, y);
                    CTX.quadraticCurveTo(x, y, x, y + radius);
                    CTX.stroke();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawRoundedRectangle(x, y, width, height, radius, borderColor, lineWidth, backgroundColor) {
                try {
                    CTX.beginPath();
                    CTX.strokeStyle = borderColor;
                    CTX.fillStyle = backgroundColor;
                    CTX.lineWidth = lineWidth;
                    CTX.moveTo(x, y + radius);
                    CTX.lineTo(x, y + height - radius);
                    CTX.quadraticCurveTo(x, y + height, x + radius, y + height);
                    CTX.lineTo(x + width - radius, y + height);
                    CTX.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
                    CTX.lineTo(x + width, y + radius);
                    CTX.quadraticCurveTo(x + width, y, x + width - radius, y);
                    CTX.lineTo(x + radius, y);
                    CTX.quadraticCurveTo(x, y, x, y + radius);
                    CTX.stroke();
                    CTX.fill();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawBorder(x, y, width, height, color, lineWidth) {
                try {
                    CTX.beginPath();
                    CTX.strokeStyle = color;
                    CTX.lineWidth = lineWidth;
                    CTX.strokeRect(x, y, width, height);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawRectangle(x, y, width, height, borderColor, lineWidth, backgroundColor) {
                try {
                    CTX.beginPath();
                    if (lineWidth != 0) CTX.strokeStyle = borderColor;
                    CTX.lineWidth = lineWidth;
                    CTX.fillStyle = backgroundColor;
                    if (lineWidth != 0) CTX.strokeRect(x, y, width, height);
                    CTX.fillRect(x, y, width, height);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawSimpleRectangle(x, y, width, height, backgroundColor) {
                try {
                    CTX.beginPath();
                    CTX.fillStyle = backgroundColor;
                    CTX.fillRect(x, y, width, height);
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _sliceDateTimes(range, datetimes) {
                try {
                    if ((range) && (datetimes)) {
                        var retDatetimes = {};
                        var rs = range.start; var re = range.end; var sv = datetimes.start; var ev = datetimes.end;
                        if ((rs <= sv) && (sv < re)) {
                            retDatetimes.start = sv;
                            if (ev <= re) retDatetimes.end = ev;
                            else retDatetimes.end = re;
                        } else if ((sv < rs) && (ev > rs)) {
                            retDatetimes.start = rs;
                            if (ev <= re) retDatetimes.end = ev;
                            else retDatetimes.end = re;
                        } else return null;
                        return retDatetimes;
                    }
                    return null;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _hasThisScale(scale, collection) {
                try {
                    //point to look [maxScale,minScale]
                    for (var i = 0; i < collection.length; i++) {
                        var scale2 = collection[i];
                        if ((scale2.MaxScale == scale.MaxScale) && (scale2.MinScale == scale.MinScale) && (scale2.Pane == scale.Pane)) return scale2;
                    }
                    return null;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawScales(table) {
                try {
                    //seek for all scales, compact scales
                    var scalesToAdd = [];
                    var incSW = DEFAULTVARIABLESCALEWIDTH;
                    var sw = 0;
                    var paneStr = '';
                    for (var i = 0; i < table.length; i++) {
                        //var lineCap = ['butt', 'round', 'square']; 
                        //WIDTH_SCALEAREA; //divide by scale count.                        
                        var cs = table[i];
                        cs.Added = 0;
                        //Extract previos Scale added
                        var ts = _hasThisScale(cs, scalesToAdd);
                        if ((ts == null) && (cs.IsDigital != 1)) {
                            scalesToAdd.push(cs);
                            cs.Added++;
                            if (paneStr.indexOf(cs.Pane) < 0) { paneStr += cs.Pane; PANECOUNT++; }
                        } else if (cs.IsDigital != 1) {
                            ts.Added++;
                            if (paneStr.indexOf(cs.Pane) < 0) { paneStr += cs.Pane; PANECOUNT++; }
                        }
                    }
                    var paneLeft = [];
                    for (var i = 0; i < scalesToAdd.length; i++) {
                        s = scalesToAdd[i];
                        if (s.Pane == null) s.Pane = 0;
                        HEIGHT_PANE = (PANECOUNT > 1) ? (HEIGHT_SCALEAREA / PANECOUNT) - (MARGINPANE * (PANECOUNT - 1)) : HEIGHT_SCALEAREA;
                        var color = (s.Color) ? s.Color : '#303030';
                        var bckColor = (s.Added > 1) ? '#303030' : color;
                        var textStyle = DEFAULTFONTSTYLE;
                        textStyle.color = '#101010';
                        //MARGINLEFT + sw
                        if (paneLeft[s.Pane] == undefined) paneLeft[s.Pane] = MARGINLEFT + sw;
                        _drawScale(HEIGHT_PANE, 8, 11, s.MinScale, s.MaxScale, true, bckColor, paneLeft[s.Pane], MARGINTOP + ((HEIGHT_PANE + MARGINPANE) * s.Pane), 'square', textStyle);
                        //sw += incSW;
                        paneLeft[s.Pane] += incSW;
                    }
                    var max_w = incSW;
                    for (var i = 0; i < paneLeft.length; i++) if (max_w < paneLeft[i]) max_w = paneLeft[i];
                    WIDTH_SCALEAREA = max_w; // (scalesToAdd.length * incSW);
                    WIDTH_AXISXSCALEAREA = WIDTH_REFERENCEAREA = WIDTH_DIGITALAREA = WIDTH_DRAWAREA = WIDTH - (WIDTH_SCALEAREA + MARGINLEFT) - MARGINRIGHT;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawGrid(vertical, horizontal, border) {
                try {
                    //HEIGHT_DRAWAREA;
                    //WIDTH_DRAWAREA;
                    if ((border) && (border.allow)) {
                        var color = (border.color) ? border.color : '#545454';
                        var rounded = (border.rounded) ? border.rounded : 5;
                        var lineWidth = (border.width) ? border.width : 1;
                        if (border.rounded) _drawRoundedBorder(WIDTH_SCALEAREA, MARGINTOP, WIDTH_DRAWAREA, HEIGHT_DRAWAREA, rounded, color, lineWidth);
                        else _drawBorder(WIDTH_SCALEAREA + MARGINLEFT, MARGINTOP, WIDTH_DRAWAREA, HEIGHT_DRAWAREA, color, lineWidth);
                    }
                    if ((vertical) && (vertical.allow)) {
                        var color = (vertical.color) ? vertical.color : COLORS.GRID.borderColor;
                        var lineWidth = (vertical.width) ? vertical.width : 0.5;
                        var cuts = (vertical.cuts) ? vertical.cuts : 10;
                        var type = (vertical.style) ? vertical.style : 'solid';
                        var incX = WIDTH_DRAWAREA / cuts;
                        var x1 = incX;
                        switch (type) {
                            case 'solid':
                                for (var i = 0; i < cuts - 1; i++) {
                                    CTX.strokeStyle = color;
                                    CTX.beginPath();
                                    CTX.lineWidth = lineWidth;
                                    CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP);
                                    CTX.lineTo(WIDTH_SCALEAREA + x1, HEIGHT_DRAWAREA + MARGINTOP);
                                    CTX.closePath();
                                    CTX.stroke();
                                    x1 += incX;
                                }
                                break;
                            case 'dotted':
                                var dSize = 12;
                                var dSpace = 12;
                                var dotted = HEIGHT_DRAWAREA / (dSize + dSpace);
                                for (var i = 0; i < cuts - 1; i++) {
                                    CTX.strokeStyle = color;
                                    var y1 = 0;
                                    for (var j = 0; j < dotted; j++) {
                                        CTX.beginPath();
                                        CTX.lineWidth = lineWidth;
                                        CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP + y1);
                                        CTX.lineTo(WIDTH_SCALEAREA + x1, MARGINTOP + (y1 + dSize));
                                        y1 += (dSize + dSpace);
                                        CTX.closePath();
                                        CTX.stroke();
                                    }
                                    x1 += incX;
                                }
                                break;
                        }
                    }
                    if ((horizontal) && (horizontal.allow)) {
                        var color = (horizontal.color) ? horizontal.color : COLORS.GRID.borderColor;
                        var lineWidth = (horizontal.width) ? horizontal.width : 0.5;
                        var cuts = (horizontal.cuts) ? horizontal.cuts : 10;
                        var type = (horizontal.style) ? horizontal.style : 'solid';
                        var incY = HEIGHT_DRAWAREA / cuts;
                        var y1 = incY;
                        switch (type) {
                            case 'solid':
                                for (var i = 0; i < cuts - 1; i++) {
                                    CTX.strokeStyle = color;
                                    CTX.beginPath();
                                    CTX.lineWidth = lineWidth;
                                    CTX.moveTo(WIDTH_SCALEAREA, MARGINTOP + y1);
                                    CTX.lineTo(WIDTH_SCALEAREA + WIDTH_DRAWAREA, MARGINTOP + y1);
                                    y1 += incY;
                                    CTX.closePath();
                                    CTX.stroke();
                                }
                                break;
                            case 'dotted':
                                var dSize = 12;
                                var dSpace = 12;
                                var dotted = WIDTH_DRAWAREA / (dSize + dSpace);
                                for (var i = 0; i < cuts - 1; i++) {
                                    CTX.strokeStyle = color;
                                    var x1 = 0;
                                    for (var j = 0; j < dotted; j++) {
                                        CTX.beginPath();
                                        CTX.lineWidth = lineWidth;
                                        CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP + y1);
                                        CTX.lineTo(WIDTH_SCALEAREA + x1 + dSize, MARGINTOP + y1);
                                        x1 += (dSize + dSpace);
                                        CTX.closePath();
                                        CTX.stroke();
                                    }
                                    y1 += incY;
                                }
                                break;
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawPaneGrid(vertical, horizontal, border) {
                try {
                    //HEIGHT_DRAWAREA;
                    //WIDTH_DRAWAREA;
                    if ((border) && (border.allow)) {
                        var color = (border.color) ? border.color : '#545454';
                        var rounded = (border.rounded) ? border.rounded : 5;
                        var lineWidth = (border.width) ? border.width : 1;
                        for (var i = 0; i < PANECOUNT; i++) {
                            if (border.rounded) _drawRoundedBorder(WIDTH_SCALEAREA, MARGINTOP + (i * (HEIGHT_PANE + MARGINPANE)), WIDTH_DRAWAREA, HEIGHT_PANE, rounded, color, lineWidth);
                            else _drawBorder(WIDTH_SCALEAREA + MARGINLEFT, MARGINTOP + (i * (HEIGHT_PANE + MARGINPANE)), WIDTH_DRAWAREA, HEIGHT_PANE, color, lineWidth);
                        }
                    }
                    if ((vertical) && (vertical.allow)) {
                        var color = (vertical.color) ? vertical.color : COLORS.GRID.borderColor;
                        var lineWidth = (vertical.width) ? vertical.width : 0.5;
                        var cuts = (vertical.cuts) ? vertical.cuts : 10;
                        var type = (vertical.style) ? vertical.style : 'solid';
                        var incX = WIDTH_DRAWAREA / cuts;
                        var x1;
                        switch (type) {
                            case 'solid':
                                for (var p = 0; p < PANECOUNT; p++) {
                                    x1 = incX;
                                    for (var i = 0; i < cuts - 1; i++) {
                                        CTX.strokeStyle = color;
                                        CTX.beginPath();
                                        CTX.lineWidth = lineWidth;
                                        CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP + (p * (HEIGHT_PANE + MARGINPANE)));
                                        CTX.lineTo(WIDTH_SCALEAREA + x1, HEIGHT_PANE + MARGINTOP + (p * (HEIGHT_PANE + MARGINPANE)));
                                        CTX.closePath();
                                        CTX.stroke();
                                        x1 += incX;
                                    }
                                }
                                break;
                            case 'dotted':
                                var dSize = 6;
                                var dSpace = 3;
                                var dotted = HEIGHT_PANE / (dSize + dSpace);
                                for (var p = 0; p < PANECOUNT; p++) {
                                    x1 = incX;
                                    for (var i = 0; i < cuts - 1; i++) {
                                        CTX.strokeStyle = color;
                                        var y1 = 0;
                                        for (var j = 0; j < dotted; j++) {
                                            CTX.beginPath();
                                            CTX.lineWidth = lineWidth;
                                            CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP + y1 + (p * (HEIGHT_PANE + MARGINPANE)));
                                            CTX.lineTo(WIDTH_SCALEAREA + x1, MARGINTOP + (y1 + dSize) + (p * (HEIGHT_PANE + MARGINPANE)));
                                            y1 += (dSize + dSpace);
                                            CTX.closePath();
                                            CTX.stroke();
                                        }
                                        x1 += incX;
                                    }
                                }
                                break;
                        }
                    }
                    if ((horizontal) && (horizontal.allow)) {
                        var color = (horizontal.color) ? horizontal.color : COLORS.GRID.borderColor;
                        var lineWidth = (horizontal.width) ? horizontal.width : 0.5;
                        var cuts = (horizontal.cuts) ? horizontal.cuts : 10;
                        var type = (horizontal.style) ? horizontal.style : 'solid';
                        var incY = HEIGHT_PANE / cuts;
                        var y1;
                        switch (type) {
                            case 'solid':
                                for (var p = 0; p < PANECOUNT; p++) {
                                    y1 = incY;
                                    for (var i = 0; i < cuts - 1; i++) {
                                        CTX.strokeStyle = color;
                                        CTX.beginPath();
                                        CTX.lineWidth = lineWidth;
                                        CTX.moveTo(WIDTH_SCALEAREA, MARGINTOP + y1 + (p * (HEIGHT_PANE + MARGINPANE)));
                                        CTX.lineTo(WIDTH_SCALEAREA + WIDTH_DRAWAREA, MARGINTOP + y1 + (p * (HEIGHT_PANE + MARGINPANE)));
                                        y1 += incY;
                                        CTX.closePath();
                                        CTX.stroke();
                                    }
                                }
                                break;
                            case 'dotted':
                                var dSize = 6;
                                var dSpace = 3;
                                var dotted = WIDTH_DRAWAREA / (dSize + dSpace);
                                for (var p = 0; p < PANECOUNT; p++) {
                                    y1 = incY;
                                    for (var i = 0; i < cuts - 1; i++) {
                                        CTX.strokeStyle = color;
                                        var x1 = 0;
                                        for (var j = 0; j < dotted; j++) {
                                            CTX.beginPath();
                                            CTX.lineWidth = lineWidth;
                                            CTX.moveTo(WIDTH_SCALEAREA + x1, MARGINTOP + y1 + (p * (HEIGHT_PANE + MARGINPANE)));
                                            CTX.lineTo(WIDTH_SCALEAREA + x1 + dSize, MARGINTOP + y1 + (p * (HEIGHT_PANE + MARGINPANE)));
                                            x1 += (dSize + dSpace);
                                            CTX.closePath();
                                            CTX.stroke();
                                        }
                                        y1 += incY;
                                    }
                                }
                                break;
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _normalizeAnalogValues(values) {
                try {
                    var v;
                    var retValues = [];
                    var id = '';
                    if ((values) && (values.length > 0)) {
                        if (XAXISISTIME) { //DateTime based
                            for (var i = 0; i < values.length; i++) {
                                var re = new RegExp(REGEXPDATE2);
                                values[i].XValue = new Date(parseFloat(re.exec(values[i].XValue)[0]));
                                values[i].TickTime = values[i].XValue.getTime();
                                id = parseInt(values[i].ID);
                                MAXVALUE_X = (values[i].TickTime > MAXVALUE_X) ? values[i].TickTime : MAXVALUE_X;
                                MINVALUE_X = (values[i].TickTime < MINVALUE_X) ? values[i].TickTime : MINVALUE_X;
                                if (retValues[id] == undefined) retValues[id] = [];
                                retValues[id].push(values[i]);
                            }
                        } else { //Number based
                            for (var i = 0; i < values.length; i++) {

                            }
                        }
                    }
                    return retValues;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _normalizeDigitalValues(values) {
                try {
                    var v;
                    var retValues = [];
                    var id = '';
                    if ((values) && (values.length > 0)) {
                        if (XAXISISTIME) { //DateTime based
                            for (var i = 0; i < values.length; i++) {
                                var re = new RegExp(REGEXPDATE2);
                                values[i].Start = new Date(parseFloat(re.exec(values[i].Start)[0]));
                                values[i].End = new Date(parseFloat(re.exec(values[i].End)[0]));
                                values[i].StartTickTime = values[i].Start.getTime();
                                values[i].EndTickTime = values[i].End.getTime();
                                id = parseInt(values[i].ID);
                                MAXVALUE_X = (values[i].StartTickTime > MAXVALUE_X) ? values[i].StartTickTime : MAXVALUE_X;
                                MINVALUE_X = (values[i].EndTickTime < MINVALUE_X) ? values[i].EndTickTime : MINVALUE_X;
                                if (retValues[id] == undefined) retValues[id] = [];
                                retValues[id].push(values[i]);
                            }
                        } else {  //Number based
                            for (var i = 0; i < values.length; i++) { }
                        }
                    }
                    return retValues;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _normalizeEvents(values) {
                try {
                    var v;
                    var retValues = [];
                    var id = '';
                    if ((values) && (values.length > 0)) {
                        for (var i = 0; i < values.length; i++) {
                            var re = new RegExp(REGEXPDATE2);
                            values[i].XValue = new Date(parseFloat(re.exec(values[i].XValue)[0]));
                            values[i].TickTime = values[i].XValue.getTime();
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _normalizeSections(values) {
                try {
                    var v;
                    var retValues = [];
                    var id = '';
                    if ((values) && (values.length > 0)) {
                        for (var i = 0; i < values.length; i++) {
                            var section = values[i];
                            var re = new RegExp(REGEXPDATE2);
                            section.Start = new Date(parseFloat(re.exec(section.Start)[0]));
                            section.End = new Date(parseFloat(re.exec(section.End)[0]));
                            section.StartTickTime = section.Start.getTime();
                            section.EndTickTime = section.End.getTime();
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _getScale(ID, scales) {
                try {
                    if (scales) {
                        for (var i = 0; i < scales.length; i++) {
                            if (scales[i].ID == ID) return scales[i];
                        }
                    }
                    return null;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawLineCurve(variable, values, pane) {
                try {
                    if (variable) {
                        var interval = (MAXVALUE_X - MINVALUE_X);
                        CTX.strokeStyle = variable.Color = (variable.Color) ? variable.Color : '#505050';
                        CTX.beginPath();
                        CTX.lineWidth = 1;
                        isFirstValue = true;
                        //HEIGHT_PANE;
                        //MARGINPANE;
                        CTX.save();
                        var x1, y1, x2, y2;
                        var curX, curY;
                        var isOut = false;
                        for (var i = 0; i < values.length; i++) {
                            //Clip the line if it is out of scale
                            if (values[i].YValue > variable.MaxScale) {
                                ///y - y1 = m(x-x1);
                                ///y = mx + b;
                                /// y - y1 + x1 =x
                                /// ------
                                ///   m

                                //var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                                //x1 = lpx + WIDTH_SCALEAREA;
                                //var tpx = HEIGHT_PANE - (((HEIGHT_PANE) * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                                //y1 = tpx + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));

                                var x2 = (WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0)) + WIDTH_SCALEAREA;
                                var y2 = (HEIGHT_PANE - (((HEIGHT_PANE) * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0)) + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                                var v1 = (x2 - x1);
                                var v2 = ((variable.MaxScale - y2) - (variable.MaxScale - y1));
                                var m = v2 / v1;
                                curX = ((variable.MaxScale - y1) / m) + x1;
                                curY = (HEIGHT_PANE - (((HEIGHT_PANE) * ((variable.MaxScale - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0)) + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                                if (!(isOut)) CTX.lineTo(curX, curY);
                                //else 
                                CTX.moveTo(x2, y2);
                                x1 = x2;
                                y1 = y2;
                                isOut = true;
                            } else if (values[i].YValue < variable.MinScale) {
                                // look in the minScale too. 
                                ///y - y1 = m(x-x1);
                                ///y = mx + b;
                                /// y - y1 + x1 =x
                                /// ------
                                ///   m

                                var x2 = (WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0)) + WIDTH_SCALEAREA;
                                var y2 = (HEIGHT_PANE - (((HEIGHT_PANE) * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0)) + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                                var v1 = (x2 - x1);
                                var v2 = ((variable.MaxScale - y2) - (variable.MaxScale - y1));
                                var m = v2 / v1;
                                curX = ((variable.MaxScale - y1) / m) + x1;
                                curY = (HEIGHT_PANE - (((HEIGHT_PANE) * ((variable.MinScale - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0)) + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                                if (!(isOut)) CTX.lineTo(curX, curY);
                                //else 
                                CTX.moveTo(x2, y2);
                                x1 = x2;
                                y1 = y2;
                                isOut = true;

                            } else {
                                if (isOut) {
                                    CTX.moveTo(x2, curY);
                                }
                                isOut = false;
                                var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                                x2 = lpx + WIDTH_SCALEAREA;
                                var tpx = HEIGHT_PANE - (((HEIGHT_PANE) * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                                y2 = tpx + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                                if (isFirstValue) { CTX.moveTo(WIDTH_SCALEAREA + lpx, tpx + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)))); isFirstValue = false; }
                                //CTX.lineTo(WIDTH_SCALEAREA + lpx, tpx + (MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE))));
                                CTX.lineTo(x2, y2);
                                x1 = x2;
                                y1 = y2;
                            }
                        }
                        CTX.stroke();
                        CTX.restore();
                        _addVariableReference(variable);
                    }
                    CTX.closePath();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawQuadraticCurve(variable, values, pane) {
                try {
                    if (variable) {
                        var interval = (MAXVALUE_X - MINVALUE_X);
                        CTX.strokeStyle = variable.Color = (variable.Color) ? variable.Color : '#505050';
                        CTX.beginPath();
                        CTX.lineWidth = 1;
                        isFirstValue = true;
                        for (var i = 0; i < values.length - 1; i++) {
                            //                            var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            //                            var tpx = HEIGHT_SCALEAREA - ((HEIGHT_SCALEAREA * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);

                            //                            var nlpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            //                            var ntpx = HEIGHT_SCALEAREA - ((HEIGHT_SCALEAREA * ((values[i + 1].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);

                            //                            if (isFirstValue) { CTX.moveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP); isFirstValue = false; }
                            //                            CTX.quadraticCurveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP, WIDTH_SCALEAREA + nlpx, ntpx + MARGINTOP);

                            var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            var tpx = HEIGHT_PANE - ((HEIGHT_PANE * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);

                            var nlpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            var ntpx = HEIGHT_PANE - ((HEIGHT_PANE * ((values[i + 1].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);

                            if (isFirstValue) { CTX.moveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE))); isFirstValue = false; }
                            CTX.quadraticCurveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)), WIDTH_SCALEAREA + nlpx, ntpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                        }
                        CTX.stroke();
                        _addVariableReference(variable);
                    }
                    CTX.closePath();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawBerzierCurve(variable, values, pane) {
                try {
                    if (variable) {
                        var interval = (MAXVALUE_X - MINVALUE_X);
                        CTX.strokeStyle = variable.Color = (variable.Color) ? variable.Color : '#505050';
                        CTX.beginPath();
                        CTX.lineWidth = 0.7;
                        isFirstValue = true;
                        for (var i = 0; i < values.length - 2; i++) {
                            //                            var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            //                            var tpx = HEIGHT_SCALEAREA - ((HEIGHT_SCALEAREA * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            //                            var nlpx = WIDTH_DRAWAREA * ((((values[i + 1].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            //                            var ntpx = HEIGHT_SCALEAREA - ((HEIGHT_SCALEAREA * ((values[i + 1].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            //                            var n2lpx = WIDTH_DRAWAREA * ((((values[i + 2].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            //                            var n2tpx = HEIGHT_SCALEAREA - ((HEIGHT_SCALEAREA * ((values[i + 2].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            //                            if (isFirstValue) { CTX.moveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP); isFirstValue = false; }
                            //                            CTX.bezierCurveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP, WIDTH_SCALEAREA + nlpx, ntpx + MARGINTOP, WIDTH_SCALEAREA + n2lpx, n2tpx + MARGINTOP);

                            var lpx = WIDTH_DRAWAREA * ((((values[i].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            var tpx = HEIGHT_PANE - ((HEIGHT_PANE * ((values[i].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            var nlpx = WIDTH_DRAWAREA * ((((values[i + 1].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            var ntpx = HEIGHT_PANE - ((HEIGHT_PANE * ((values[i + 1].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            var n2lpx = WIDTH_DRAWAREA * ((((values[i + 2].TickTime - MINVALUE_X) * 100) / (interval)) / 100.0);
                            var n2tpx = HEIGHT_PANE - ((HEIGHT_PANE * ((values[i + 2].YValue - variable.MinScale) * 100 / (variable.MaxScale - variable.MinScale))) / 100.0);
                            if (isFirstValue) { CTX.moveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE))); isFirstValue = false; }
                            CTX.bezierCurveTo(WIDTH_SCALEAREA + lpx, tpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)), WIDTH_SCALEAREA + nlpx, ntpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)), WIDTH_SCALEAREA + n2lpx, n2tpx + MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)));
                        }
                        CTX.stroke();
                        _addVariableReference(variable);
                    }
                    CTX.closePath();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawEventBox(x, y, backgroundColor, size, text, textFontStyle) {
                var h = w = size * EVENTSIZEFACTOR;
                var x1 = x - (w / 2);
                var y1 = y - (h / 2);
                _drawSimpleRectangle(x1, y1, w, h, backgroundColor);
                if (text) {
                    CTX.drawText(text, x1 + (w + 2), y1 + (h / 1.3), textFontStyle);
                }
            };
            function _drawEventCircle(x, y, backgroundColor, size, text, textFontStyle) {
                CTX.beginPath();
                CTX.fillStyle = backgroundColor;
                var h = w = size * EVENTSIZEFACTOR;
                var circSize = (size * EVENTSIZEFACTOR) / 2.0;
                var x1 = x;
                var y1 = y;
                CTX.arc(x1, y1, circSize, 0, Math.PI * 2, true);
                CTX.fill();
                CTX.closePath();
                if (text) {
                    CTX.drawText(text, x1 + w - 1, y1 + 2, textFontStyle);
                }
            };
            function _drawEventTriangle(x, y, backgroundColor, size, text, textFontStyle) {
                CTX.beginPath();
                CTX.fillStyle = backgroundColor;
                var h = w = size * EVENTSIZEFACTOR;
                var x1 = x - (w / 2);
                var y1 = y - (h / 2);
                CTX.moveTo(x1, y1);
                CTX.lineTo(x1 + w, y1);
                CTX.lineTo(x1 + (w / 2), y1 + h);
                CTX.fill();
                CTX.closePath();
                if (text) {
                    CTX.drawText(text, x1 + (w + 2), y1 + (h / 1.3), textFontStyle);
                }
            };
            function _drawEvents(values, allowText) {
                try {
                    if (values) {
                        var y = MARGINTOP + 25;
                        var e = null;
                        var x = 0;
                        var size = 5;
                        var minDistance;
                        var textWidthSize = 110;
                        if (allowText) minDistance = size * EVENTSIZEFACTOR + 10 + textWidthSize;
                        else minDistance = size * EVENTSIZEFACTOR + 10;
                        var diff = -1;
                        var cx = 0;
                        var text;
                        var tfs = DEFAULTFONTSTYLE;
                        tfs.angle = 0;
                        tfs.size = 6;
                        tfs.color = '#252525';
                        tfs.weight = 1.5;
                        for (var i = 0; i < values.length; i++) {
                            //Time, TickTime
                            e = values[i];
                            if ((MINVALUE_X <= e.TickTime) && (e.TickTime <= MAXVALUE_X)) {
                                x = (WIDTH_DRAWAREA * ((((e.TickTime - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                if (allowText) text = _cutStringText(e.Description.toString(), textWidthSize, tfs); else text = null;
                                if (minDistance < (x - cx)) {
                                    y = MARGINTOP + 25;
                                    cx = x;
                                } else y += 14;
                                switch (e.EventType) {
                                    case 1:
                                        _drawEventTriangle(x - ((size * EVENTSIZEFACTOR) / 2), y, COLORS.EVENTS[1].fillColor, size, text, tfs);
                                        break;
                                    case 2:
                                        _drawEventCircle(x - ((size * EVENTSIZEFACTOR) / 2), y, COLORS.EVENTS[2].fillColor, size, text, tfs);
                                        break;
                                    case 3:
                                        _drawEventBox(x - ((size * EVENTSIZEFACTOR) / 2), y, COLORS.EVENTS[3].fillColor, size, text, tfs);
                                        break;
                                    case 4:
                                    case 5:
                                    default:
                                        _drawEventTriangle(x - ((size * EVENTSIZEFACTOR) / 2), y, COLORS.EVENTS[0].fillColor, size, text, tfs);
                                        break;
                                }
                            }
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawPaneEvents(values, allowText) {
                try {
                    if (values) {
                        var y = MARGINTOP + 25;
                        var e = null;
                        var x = 0;
                        var size = 5;
                        var minDistance;
                        var textWidthSize = 110;
                        if (allowText) minDistance = size * EVENTSIZEFACTOR + 10 + textWidthSize;
                        else minDistance = size * EVENTSIZEFACTOR + 10;
                        var diff = -1;
                        var cx = 0;
                        var text;
                        var tfs = DEFAULTFONTSTYLE;
                        tfs.angle = 0;
                        tfs.size = 6;
                        tfs.color = '#252525';
                        tfs.weight = 1.5;
                        for (var i = 0; i < values.length; i++) {
                            //Time, TickTime
                            e = values[i];
                            var pane = (e.Pane) ? e.Pane : 0;
                            if ((MINVALUE_X <= e.TickTime) && (e.TickTime <= MAXVALUE_X)) {
                                x = (WIDTH_DRAWAREA * ((((e.TickTime - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                if (allowText) text = _cutStringText(e.Description.toString(), textWidthSize, tfs); else text = null;
                                if (minDistance < (x - cx)) {
                                    y = MARGINTOP + 25;
                                    cx = x;
                                } else y += 14;
                                switch (e.EventType) {
                                    case 1:
                                        _drawEventTriangle(x - ((size * EVENTSIZEFACTOR) / 2), y + (pane * (HEIGHT_PANE + MARGINPANE)), COLORS.EVENTS[1].fillColor, size, text, tfs);
                                        break;
                                    case 2:
                                        _drawEventCircle(x - ((size * EVENTSIZEFACTOR) / 2), y + (pane * (HEIGHT_PANE + MARGINPANE)), COLORS.EVENTS[2].fillColor, size, text, tfs);
                                        break;
                                    case 3:
                                        _drawEventBox(x - ((size * EVENTSIZEFACTOR) / 2), y + (pane * (HEIGHT_PANE + MARGINPANE)), COLORS.EVENTS[3].fillColor, size, text, tfs);
                                        break;
                                    case 4:
                                    case 5:
                                    default:
                                        _drawEventTriangle(x - ((size * EVENTSIZEFACTOR) / 2), y + (pane * (HEIGHT_PANE + MARGINPANE)), COLORS.EVENTS[0].fillColor, size, text, tfs);
                                        break;
                                }
                            }
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawDigitalPanel() {
                try {
                    //The height value, must be calculated depending of the counted elements.
                    _drawBorder(WIDTH_SCALEAREA + MARGINLEFT, HEIGHT_DRAWAREA + HEIGHT_AXISXSCALEAREA + MARGINTOP, WIDTH_DIGITALAREA, HEIGHT_DIGITALAREA, 'rgba(50,50,50,0.2)', 1);
                    ISDIGITALPANELACTIVE = true;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _cutStringText(str, maxWidth, fontStyle) {
                try {
                    var tempW;
                    for (var i = str.length; i > 0; i--) {
                        tempW = CanvasText.getDimensions(str, fontStyle).width;
                        if ((tempW + 5) < maxWidth) return (str + '.');
                        else {
                            str = str.substring(0, str.length - 1);
                        }
                    }
                    return (str + '.');
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawDigitalLine(variable, values) {
                try {
                    //DIGITAL_Y
                    //MARGIN_Y
                    var p1 = null; var cc = null; var b = false; var h = 9;
                    var y = (HEIGHT_DRAWAREA + HEIGHT_AXISXSCALEAREA + MARGINTOP + DIGITAL_Y);
                    var textFontStyle = DEFAULTFONTSTYLE;
                    textFontStyle.size = 8;
                    textFontStyle.weight = 1.2;
                    textFontStyle.color = '#252525';
                    //Draw thin line
                    var tempW = CanvasText.getDimensions(variable.Name.toString(), textFontStyle).width + 5;
                    CTX.drawText(variable.Name.toString(), WIDTH_SCALEAREA + MARGINLEFT - tempW, y + 7, textFontStyle);

                    CTX.beginPath();
                    CTX.strokeStyle = '#505050';
                    CTX.lineWidth = 1;
                    CTX.moveTo(WIDTH_SCALEAREA, y + (h / 2));
                    CTX.lineTo(MARGINLEFT + WIDTH_SCALEAREA + WIDTH_DIGITALAREA, y + (h / 2));
                    CTX.stroke();
                    CTX.closePath();
                    var ml = 5;
                    textFontStyle.size = 6;
                    textFontStyle.weight = 2;
                    textFontStyle.color = '#FFFFFF';
                    if (values) {
                        for (var i = 0; i < values.length; i++) {
                            var color = variable.Color = (variable.Color) ? variable.Color : COLORS.LINES[i].fillColor;
                            //Not neccesary, because the MIN and MAX limit, were calculate using analogs and digitals values.
                            //var range = {}; range.start = MINVALUE_X; range.end = MAXVALUE_X;
                            //var dates = {}; dates.start = section.StartTickTime; dates.end = section.EndTickTime;
                            //dates = _sliceDateTimes(range, dates);
                            var x1 = (WIDTH_DRAWAREA * ((((values[i].StartTickTime - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                            var x2 = (WIDTH_DRAWAREA * ((((values[i].EndTickTime - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                            tempW = CanvasText.getDimensions(values[i].Description.toString(), textFontStyle).width;
                            _drawRectangle(x1, y, x2 - x1, h, '#303030', 1, color);
                            var text = values[i].Description.toString();
                            if (tempW > (x2 - x1)) text = _cutStringText(text, x2 - x1, textFontStyle);
                            CTX.drawText(text, x1 + ml, y + 7, textFontStyle);
                        }
                        //_addVariableReference(variable);
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawDigitalCurves(variables, values) {
                try {
                    //Digital Variables 
                    HEIGHT_DIGITALAREA = 12;
                    for (var i = 0; i < variables.length; i++) {
                        var v = variables[i];
                        if ((v.IsDigital) && (v.IsDigital == 1)) {
                            if (values[v.ID]) _drawDigitalLine(v, values[v.ID]);
                            DIGITAL_Y += 15;
                            HEIGHT_DIGITALAREA += 15
                        }
                    }
                    if (!ISDIGITALPANELACTIVE) _drawDigitalPanel();
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _addVariableReference(variable) {
                try {
                    if (variable) {
                        VARIABLELIST.push({ Title: variable.Name, Color: variable.Color });
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawVariablesReferences(location, type) {
                try {
                    //#8f8f8f;
                    //#C8C6C8;
                    var mr = 15;
                    var fmt = 5;
                    var lineHeight = 18;
                    var x, y, w, h;
                    var textFontStyle = DEFAULTFONTSTYLE;
                    textFontStyle.angle = 0;
                    textFontStyle.color = '#303030';
                    var tempW;
                    switch (location) {
                        case 'inside':
                            if (type == 'box') {
                                var columns = 2;
                                var boxMarginLeft = 13;
                                var boxMarginRight = 3;
                                //var boxWidth = 10;
                                var wc = 80;
                                var t = parseInt(VARIABLELIST.length / 2);
                                var j = 1;
                                w = ((wc + boxMarginLeft + boxMarginRight) * columns) + fmt;
                                x = MARGINLEFT + WIDTH_SCALEAREA + WIDTH_REFERENCEAREA - (w + mr);
                                y = MARGINTOP + mr; // +50;
                                h = (t * lineHeight) + fmt;
                                _drawRoundedRectangle(x, y, w, h, 3, '#8F8F8F', 1, 'rgba(255,255,255,0.3)');
                                var tw = 0;
                                textFontStyle.size = 6; textFontStyle.weight = 1.5;
                                for (var i = 0; i < VARIABLELIST.length; i++) {
                                    textFontStyle.color = DEFAULTFONTSTYLE.color;
                                    //textFontStyle.color = 'rgba(30,30,30,0.5)';
                                    tempW = CanvasText.getDimensions(VARIABLELIST[i].Title.toString(), textFontStyle).width;
                                    tw = (tempW > tw) ? tempW : tw;
                                    _drawRoundedRectangle(x + fmt, (y + fmt), 10, 10, 2, '#606060', 1, VARIABLELIST[i].Color);
                                    CTX.drawText(VARIABLELIST[i].Title.toString(), x + fmt + boxMarginLeft, (y + fmt + (lineHeight / 2)), textFontStyle);
                                    if (j < t) {
                                        y += lineHeight;
                                    } else if (j == t) {
                                        y = MARGINTOP + mr;
                                        x += tempW + fmt + boxMarginLeft + boxMarginRight;
                                        j = 0;
                                        tempW = 0;
                                    }
                                    j++;
                                }
                            } else {
                                var columns = 2;
                                var wc = 80;
                                var t = parseInt(VARIABLELIST.length / 2);
                                var j = 1;
                                w = (wc * columns) + fmt;
                                x = MARGINLEFT + WIDTH_SCALEAREA + WIDTH_REFERENCEAREA - (w + mr);
                                y = MARGINTOP + mr; // +50;
                                h = (t * lineHeight) + fmt;
                                _drawRoundedRectangle(x, y, w, h, 3, '#8F8F8F', 1, 'rgba(255,255,255,0.8)');
                                var tw = 0;
                                textFontStyle.size = 6; textFontStyle.weight = 1.5;
                                for (var i = 0; i < VARIABLELIST.length; i++) {
                                    textFontStyle.color = VARIABLELIST[i].Color;
                                    tempW = CanvasText.getDimensions(VARIABLELIST[i].Title.toString(), textFontStyle).width;
                                    tw = (tempW > tw) ? tempW : tw;
                                    CTX.drawText(VARIABLELIST[i].Title.toString(), x + fmt, (y + fmt + (lineHeight / 2)), textFontStyle);
                                    if (j < t) {
                                        y += lineHeight;
                                    } else if (j == t) {
                                        y = MARGINTOP + mr;
                                        x += tempW + fmt;
                                        j = 0;
                                        tempW = 0;
                                    }
                                    j++;
                                }
                            }
                            break;
                        case 'outside':
                            if (type == 'box') {
                                var boxMarginLeft = 10;
                                var boxMarginRight = 3;
                                var boxWidth = 10;
                                x = MARGINLEFT + WIDTH_SCALEAREA;
                                y = MARGINTOP + HEIGHT_DRAWAREA + HEIGHT_AXISXSCALEAREA + HEIGHT_DIGITALAREA + mr;
                                w = WIDTH_REFERENCEAREA;
                                h = HEIGHT_REFERENCEAREA;
                                textFontStyle.size = 8; textFontStyle.weight = 1.3;
                                _drawRoundedRectangle(x, y, w, h, 3, '#8F8F8F', 1, 'rgba(255,255,255,0.8)');
                                var remaing = w;
                                for (var i = 0; i < VARIABLELIST.length; i++) {
                                    textFontStyle.color = DEFAULTFONTSTYLE.color;
                                    tempW = CanvasText.getDimensions(VARIABLELIST[i].Title.toString(), textFontStyle).width;
                                    remaing -= (tempW + fmt + boxMarginLeft + boxMarginRight + boxWidth);
                                    if (remaing < 0) { x = MARGINLEFT + WIDTH_SCALEAREA; y += lineHeight; remaing = w; }
                                    _drawRoundedRectangle(x + fmt, (y + fmt), 10, 10, 2, '#606060', 1, VARIABLELIST[i].Color);
                                    CTX.drawText(VARIABLELIST[i].Title.toString(), x + fmt + boxMarginRight + boxWidth, (y + fmt + (lineHeight / 2)), textFontStyle);
                                    x += (tempW + fmt + boxMarginLeft + boxWidth);
                                }

                            } else {
                                x = MARGINLEFT + WIDTH_SCALEAREA;
                                y = MARGINTOP + HEIGHT_DRAWAREA + HEIGHT_AXISXSCALEAREA + HEIGHT_DIGITALAREA + mr;
                                w = WIDTH_REFERENCEAREA;
                                h = HEIGHT_REFERENCEAREA;
                                textFontStyle.size = 8; textFontStyle.weight = 1.3;
                                _drawRoundedRectangle(x, y, w, h, 3, '#8F8F8F', 1, 'rgba(255,255,255,0.8)');
                                var remaing = w;
                                for (var i = 0; i < VARIABLELIST.length; i++) {
                                    textFontStyle.color = VARIABLELIST[i].Color;
                                    tempW = CanvasText.getDimensions(VARIABLELIST[i].Title.toString(), textFontStyle).width;
                                    remaing -= (tempW + fmt);
                                    if (remaing < 0) { x = MARGINLEFT + WIDTH_SCALEAREA; y += lineHeight; remaing = w; }
                                    CTX.drawText(VARIABLELIST[i].Title.toString(), x + fmt, (y + fmt + (lineHeight / 2)), textFontStyle);
                                    x += tempW + fmt;
                                }
                            }
                            break;
                    };

                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawSections(values) {
                try {
                    if (values) {
                        var previousXText = 0;
                        var currentXText = 0;
                        var staggered = 0;
                        var tempW;
                        var textFontStyle = DEFAULTFONTSTYLE;
                        var section;
                        var range;
                        var dates;
                        var x1;
                        var x2;
                        for (var i = 0; i < values.length; i++) {
                            section = values[i];
                            range = {}; range.start = MINVALUE_X; range.end = MAXVALUE_X;
                            dates = {}; dates.start = section.StartTickTime; dates.end = section.EndTickTime;
                            textFontStyle.size = 8; textFontStyle.weight = 1.3;
                            textFontStyle.angle = 0;
                            dates = _sliceDateTimes(range, dates);
                            if (dates != null) {
                                var r = parseInt(Math.random() * COLORS.SECTIONS.length);
                                var backgroundColor = (section.BackgroundColor) ? section.BackgroundColor : COLORS.SECTIONS[r].fillColor;
                                var borderColor = (section.BorderColor) ? section.BorderColor : COLORS.SECTIONS[r].borderColor;
                                var borderThickness = (section.BorderThickness) ? section.BorderThickness : 0;
                                x1 = (WIDTH_DRAWAREA * ((((dates.start - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                x2 = (WIDTH_DRAWAREA * ((((dates.end - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                //_drawSimpleRectangle(x1, MARGINTOP, x2 - x1, HEIGHT_DRAWAREA, backgroundColor);                                
                                _drawRectangle(x1, MARGINTOP, x2 - x1, HEIGHT_DRAWAREA, borderColor, borderThickness, backgroundColor);
                                tempW = CanvasText.getDimensions(section.Title.toString(), textFontStyle).width;
                                currentXText = (x1 + (((x2 - x1) - tempW) / 2));
                                if (currentXText > (previousXText + 12)) staggered = 0;
                                else staggered = -12;
                                CTX.drawText(section.Title.toString(), currentXText, (MARGINTOP - 3) + staggered, textFontStyle);
                                if (staggered == 0) previousXText = currentXText + tempW;
                            }
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawPaneSections(values) {
                try {
                    if (values) {
                        var previousXText = 0;
                        var currentXText = 0;
                        var staggered = 0;
                        var tempW;
                        var textFontStyle = DEFAULTFONTSTYLE;
                        var section;
                        var range;
                        var dates;
                        var x1;
                        var x2;
                        for (var i = 0; i < values.length; i++) {
                            section = values[i];
                            range = {}; range.start = MINVALUE_X; range.end = MAXVALUE_X;
                            dates = {}; dates.start = section.StartTickTime; dates.end = section.EndTickTime;
                            textFontStyle.size = 7; textFontStyle.weight = 1.3;
                            textFontStyle.angle = 0;
                            dates = _sliceDateTimes(range, dates);
                            var pane = (section.Pane) ? section.Pane : 0;
                            if (dates != null) {
                                var r = parseInt(Math.random() * COLORS.SECTIONS.length);
                                var backgroundColor = (section.BackgroundColor) ? section.BackgroundColor : COLORS.SECTIONS[r].fillColor;
                                var borderColor = (section.BorderColor) ? section.BorderColor : COLORS.SECTIONS[r].borderColor;
                                var borderThickness = (section.BorderThickness) ? section.BorderThickness : 0;
                                x1 = (WIDTH_DRAWAREA * ((((dates.start - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                x2 = (WIDTH_DRAWAREA * ((((dates.end - MINVALUE_X) * 100) / (MAXVALUE_X - MINVALUE_X)) / 100.0)) + MARGINLEFT + WIDTH_SCALEAREA;
                                //_drawSimpleRectangle(x1, MARGINTOP, x2 - x1, HEIGHT_DRAWAREA, backgroundColor);                                
                                _drawRectangle(x1, MARGINTOP + (pane * (HEIGHT_PANE + MARGINPANE)), x2 - x1, HEIGHT_PANE, borderColor, borderThickness, backgroundColor);
                                tempW = CanvasText.getDimensions(section.Title.toString(), textFontStyle).width;
                                currentXText = (x1 + (((x2 - x1) - tempW) / 2));
                                if (currentXText > (previousXText + 12)) staggered = 0;
                                else staggered = -12;
                                CTX.drawText(section.Title.toString(), currentXText, (MARGINTOP - 2) + staggered + (pane * (HEIGHT_PANE + MARGINPANE)), textFontStyle);
                                if (staggered == 0) previousXText = currentXText + tempW;
                            }
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _drawAnalogCurves(variables, values) {
                try {
                    //draw clip area                   
                    //                    CTX.beginPath();
                    //                    CTX.fillStyle = 'rgba(255,0,0,0.1)';
                    //                    var xc, yc, wc, hc;
                    //                    xc = (WIDTH_SCALEAREA + MARGINLEFT);
                    //                    yc = (MARGINTOP);
                    //                    wc = (WIDTH_DRAWAREA);
                    //                    hc = (HEIGHT_PANE);
                    //                    CTX.fillRect(xc, yc, wc, hc);
                    //                    CTX.clip();
                    //                    CTX.stroke();
                    //                    CTX.closePath();

                    var av = values;
                    var interval = (MAXVALUE_X - MINVALUE_X);
                    //Using line                    
                    var isFirstValue;
                    for (var index = 0; index < variables.length; index++) {
                        var s = variables[index];
                        var type = (s.Style) ? LINETYPES[s.Style] : LINETYPES[DEFAULTLINESTYLE];
                        if (s.IsDigital != 1) {
                            //Analogic Variables
                            CTX.save();
                            switch (type) {
                                case 'line':
                                    if ((av[s.ID] != undefined) && (av[s.ID].length > 0)) _drawLineCurve(s, av[s.ID], s.Pane);
                                    break;
                                case 'quadratic':
                                    if ((av[s.ID] != undefined) && (av[s.ID].length > 0)) _drawQuadraticCurve(s, av[s.ID], s.Pane);
                                    break;
                                case 'bezier':
                                    if ((av[s.ID] != undefined) && (av[s.ID].length > 0)) _drawBerzierCurve(s, av[s.ID], s.Pane);
                                    break;
                            };
                            CTX.restore();
                        }
                    }

                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _loadInitialOptions(array) {
                try {
                    if (array) {
                        for (var i = 0; i < array.length; i++) {
                            var kvp = array[i];
                            switch (kvp.Name.toUpperCase()) {
                                case 'XAXISISTIME':
                                    XAXISISTIME = (kvp.Value.toUpperCase() == 'TRUE') ? true : false;
                                    break;
                            };
                        }
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            };
            function _redraw(data, isOnlyDraw) {
                try {
                    if (data) {
                        /* --------------------------------- */
                        /* ********************************* */

                        //Load Options
                        _loadInitialOptions(data.Options);

                        //Normalize According XAXISISTIME;
                        if (!(isOnlyDraw)) {
                            data.normalizeAnalogValues = _normalizeAnalogValues(data.AnalogValues);
                            data.normalizeDigitalValues = _normalizeDigitalValues(data.DigitalValues);
                        }
                        var verticalDivisions = 20;
                        //Draw tables in order, to prevent overlapping between colors;
                        /* -----------  Scales  ------------ */
                        _drawScales(data.Variables);
                        _drawAxisXScale(7, WIDTH_DRAWAREA, 1, verticalDivisions, MINVALUE_X, MAXVALUE_X, true, '#101010', WIDTH_SCALEAREA, HEIGHT_DRAWAREA + MARGINTOP, 'square', null);
                        /* --------------------------------- */

                        /* ----------  Sections ------------ */
                        if (!(isOnlyDraw)) _normalizeSections(data.Sections);
                        if (PANECOUNT == 1) _drawSections(data.Sections);
                        else _drawPaneSections(data.Sections);
                        /* --------------------------------- */

                        /* ------------  Grid  ------------- */
                        if (PANECOUNT == 1) {
                            var vertical = { allow: true, cuts: verticalDivisions - 1, color: 'rgba(180,195,231,0.8)', lineWidth: 1, style: 'dotted' };
                            var horizontal = { allow: true, cuts: 10, color: 'rgba(157,177,228,0.7)', lineWidth: 1, style: 'dotted' };
                            var borderStyle = { allow: true, color: '#545454' /*,rounded:5*/, lineWidth: 1 };
                            _drawGrid(vertical, horizontal, borderStyle);
                        } else {
                            var vertical = { allow: true, cuts: verticalDivisions - 1, color: 'rgba(180,195,231,0.8)', lineWidth: 1, style: 'dotted' };
                            var horizontal = { allow: true, cuts: 5, color: 'rgba(157,177,228,0.7)', lineWidth: 1, style: 'dotted' };
                            var borderStyle = { allow: true, color: '#545454' /*,rounded:5*/, lineWidth: 1 };
                            _drawPaneGrid(vertical, horizontal, borderStyle);
                        }
                        /* --------------------------------- */

                        /* ----------  Length Based -------- */
                        /* --------------------------------- */

                        /* ----------  Events -------------- */
                        if (!(isOnlyDraw)) _normalizeEvents(data.Events);
                        if (PANECOUNT == 1) _drawEvents(data.Events, true);
                        else _drawPaneEvents(data.Events, true);
                        /* --------------------------------- */

                        /* ----------  Curves  ------------- */
                        //Digitals
                        _drawDigitalCurves(data.Variables, data.normalizeDigitalValues);
                        //Analogs                        
                        _drawAnalogCurves(data.Variables, data.normalizeAnalogValues);
                        /* --------------------------------- */

                        /* ----------  References ---------- */
                        _drawVariablesReferences('outside', 'box');

                        /* --------------------------------- */
                        if (!(isOnlyDraw)) PRIVATEPARAMETERS.requestData = data;
                        ISCOMPLETE = true;
                        setTimeout(PRIVATEPARAMETERS.events.onComplete, 1);
                    }
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
                setTimeout(function () { ISCOMPLETE = true; }, 200);
                PRIVATEPARAMETERS.events.onComplete();
            };

            function _parseColor(str) {
                try {
                    if (str instanceof Flotr.Color) return str;
                    var result, Color = Flotr.Color;
                    // rgb(num,num,num)
                    if ((result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)))
                        return new Color(parseInt(result[1]), parseInt(result[2]), parseInt(result[3]));
                    // rgba(num,num,num,num)
                    if ((result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)))
                        return new Color(parseInt(result[1]), parseInt(result[2]), parseInt(result[3]), parseFloat(result[4]));
                    // rgb(num%,num%,num%)
                    if ((result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)))
                        return new Color(parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55);
                    // rgba(num%,num%,num%,num)
                    if ((result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)))
                        return new Color(parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55, parseFloat(result[4]));
                    // #ffffff
                    if ((result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)))
                        return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16));
                    // #fff
                    if ((result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)))
                        return new Color(parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16));
                    // Otherwise, we're most likely dealing with a named color.
                    var name = str.strip().toLowerCase();
                    if (name == 'transparent') return new Color(255, 255, 255, 0);
                    return ((result = Color.lookupColors[name])) ? new Color(result[0], result[1], result[2]) : false;
                } catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
            }
            _initialize(object, options);

            //Public Members
            /*----------------------------*/
            /* ----Getters and Setters----*/
            /*----------------------------*/
            this.getDatabaseEngine = function () { return DatabaseConnection.GetDatabaseEngine(PRIVATEPARAMETERS.DatabaseParameters.DBEngine); };
            this.setDatabaseEngine = function (newDatabaseEngine) { DatabaseConnection.SetDatabaseEngine(PRIVATEPARAMETERS, newDatabaseEngine); };
            this.getConnectionStringName = function () { return DatabaseConnection.GetConnectionStringName(PRIVATEPARAMETERS.DatabaseParameters.ConnectionStringName); };
            this.setConnectionStringName = function (newConnectionStringName) { DatabaseConnection.SetConnectionStringName(PRIVATEPARAMETERS, newConnectionStringName); };
            this.getTablesCount = function () { return DatabaseConnection.GetTablesCount(PRIVATEPARAMETERS.DatabaseParameters.TablesCount); };
            this.setTablesCount = function (newTablesCount) { DatabaseConnection.SetTablesCount(PRIVATEPARAMETERS, newTablesCount); };
            this.getProcedure = function () { return DatabaseConnection.GetProcedure(PRIVATEPARAMETERS.DatabaseParameters.Procedure); };
            this.setProcedure = function (newProcedure) { DatabaseConnection.SetProcedure(PRIVATEPARAMETERS, newProcedure); };
            this.refreshData = function (parameters) {
                _refreshData(parameters);
            };
            this.resizeChart = function () {
                _resizeChart();
            };
        }
        catch (Error) { PRIVATEPARAMETERS.events.onError(Error); }
    };
    window.IndustrialChart = function (object, options) { return new _IndustrialChart(object, options); }
})();